home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 17
/
CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso
/
CUCD
/
Programming
/
DiceSource
/
src
/
dc1
/
asm1.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-09-09
|
60KB
|
2,744 lines
/*
* (c)Copyright 1992-1997 Obvious Implementations Corp. Redistribution and
* use is allowed under the terms of the DICE-LICENSE FILE,
* DICE-LICENSE.TXT.
*/
/*
* ASM1.C
*
* Contains routine that actually generate assembly output. Any given
* asm_ routine is only allow to allocate one data and one address register
*
* Note: avoid storing a temporary into the destination and then running an
* operation on the destination when one of the sources uses the destination.
* e.g. ptr = *ptr + 2
*/
/*
** $Filename: asm1.c $
** $Author: dice $
** $Revision: 30.326 $
** $Date: 1995/12/24 06:09:34 $
** $Log: asm1.c,v $
* Revision 30.326 1995/12/24 06:09:34 dice
* .
*
* Revision 30.156 1995/01/11 05:04:36 dice
* Added GlobalRegReserved, which serves real function and is also used to
* implement the MINIDICE hacker-did-the-obvious-thing thing.
*
* Revision 30.6 1994/08/04 04:49:26 dice
* .
*
* Revision 30.5 1994/06/13 18:37:20 dice
* rco_move returned bad value
*
* Revision 30.0 1994/06/10 18:04:45 dice
* .
*
* Revision 1.5 1994/04/15 21:16:17 jtoebes
* Fixed code for dynamic stack allocations.
*
* Revision 1.4 1993/11/22 00:28:36 jtoebes
* Final cleanup to eliminate all cerror() messages with strings.
*
* Revision 1.3 1993/11/14 21:37:04 jtoebes
* FIXED BUG01140 - DC1 is misaligning structure sizes.
* Output a ds.w 0 to align strings to a word boundary.
*
*
**/
#include "defs.h"
#include "asm.h"
Prototype long AsmState;
Prototype long LabelReturn;
Prototype long LabelProfBeg;
Prototype long LabelProfEnd;
Prototype long LabelRegsUsed;
Prototype long RegReserved;
Prototype long GlobalRegReserved;
Prototype char LastSectBuf[256];
Prototype char *LastSectName;
Prototype char *SegNames[];
Prototype char *SegTypes[];
Prototype short OutAlign;
char *SegNames[] = { "text", "data", "fardata", "bss", "farbss", "altdata", "altbss", "altcode", "combss", "farcombss" };
char *SegTypes[] = { "code", "data", "data", "bss", "bss" , "data", "bss", "code", "common", "common" };
long AsmState = 0; /* includes msb masking bits */
long LabelStackUsed;
long LabelRegsUsed;
long LabelReturn;
long LabelRegMaskUsed;
long LabelArgsBytes;
long LabelProfBeg;
long LabelProfEnd;
long RegReserved;
long GlobalRegReserved = RF_A5|RF_A7;
char LastSectBuf[256];
char *LastSectName;
short OutAlign;
short OAlgnReserved;
static Stor SOff = { ST_RelReg, RB_SP, 4 };
static Stor SPush = { ST_Push, 0, 4 };
static Stor SCallerBlk = { ST_RelArg, RB_FP, 4 };
Prototype void asm_extern(Var *);
Prototype void asm_export(Symbol *);
Prototype void asm_procbegin(Var *);
Prototype void asm_regentry(Var *);
Prototype void asm_proclink(Var *);
Prototype void asm_procend(Var *, short);
Prototype void asm_returnstorage(Exp *);
Prototype void asm_dynamictag(Var *); /* XXX */
Prototype void asm_segment(Var *);
Prototype void asm_align(long);
Prototype void asm_ds(Symbol *, long);
Prototype void asm_string(long, ubyte *, long, long, long);
Prototype void asm_label(long);
Prototype void asm_branch(long);
Prototype void asm_condbra(short, long);
Prototype long asm_push(Exp *, Type *, Stor *);
Prototype void asm_push_mask(long);
Prototype long asm_stackbytes(Type *);
Prototype void asm_call(Exp *, Stor *, Type *, Stor *, long, short);
Prototype void asm_pop(long);
Prototype void asm_pop_mask(long);
Prototype void asm_ext(Exp *, Stor *, Stor *, long);
Prototype void asm_test(Exp *, Stor *);
Prototype void asm_test_scc(Exp *, long, Stor *, short, Stor *);
Prototype void asm_cond_scc(Exp *, long, Stor *, Stor *, short *, Stor *);
Prototype void asm_sccb(Exp *, Stor *, short, short);
Prototype void asm_clr(Exp *, Stor *);
Prototype void asm_movei(Exp *, long, Stor *);
Prototype void asm_neg(Exp *, Stor *, Stor *);
Prototype void ThreeOp(Exp *, Stor *, Stor *, Stor *, void (*)(Exp *, Stor *, Stor *, Stor *));
Prototype void asm_add(Exp *, Stor *, Stor *, Stor *);
Prototype void asm_sub(Exp *, Stor *, Stor *, Stor *);
Prototype void asm_xor(Exp *, Stor *, Stor *, Stor *);
Prototype void asm_and(Exp *, Stor *, Stor *, Stor *);
Prototype void asm_or(Exp *, Stor *, Stor *, Stor *);
Prototype void asm_test_and(Exp *, Stor *, Stor *);
Prototype void asm_switch(Exp *, long, long *, long *, long);
Prototype long SizeFit(long);
Prototype long SizeFitSU(long, Stor *);
Prototype void asm_end(void);
Prototype long asm_rcomove(Exp *, char *, short, Stor *);
Prototype void asm_exg(Stor *, Stor *);
Prototype long RegCallOrder(Type *, char *, char *);
Prototype void AutoAssignRegisteredArgs(Var **, short);
Local void asm_gen_logic_class(Exp *, char *, char *, long, Stor *, Stor *, Stor *, long);
Local void SortCases(Exp *, long *, long *, long);
Local void SubDivideSwitch(Exp *, Stor *, Stor *, long, long, long, long, long *, long *);
Local void extop(long, long, Stor *);
void
asm_extern(var)
Var *var;
{
if (var->var_Stor.st_Flags & SF_REGARGSUSED)
printf("\txref\t@%s\n", SymToString(var->Sym));
printf("\txref\t_%s\n", SymToString(var->Sym));
}
void
asm_export(sym)
Symbol *sym;
{
printf("\txdef\t_%s\n", SymToString(sym));
}
void
asm_procbegin(var)
Var *var;
{
char *prefix;
LabelStackUsed = AllocLabel();
LabelReturn = AllocLabel();
LabelRegsUsed = AllocLabel();
LabelRegMaskUsed= AllocLabel();
LabelArgsBytes = AllocLabel();
/*
* prefix, TF_REGCALL uses @
* TF_STKCALL uses _
*/
if ((var->Flags & TF_REGCALL) && (var->Flags & TF_STKCALL))
{
dbprintf(("RegCall & StkCall!")); /* XXX */
Assert(0);
}
if (var->Flags & TF_REGCALL)
prefix = "@";
else
prefix = "_";
/*
* autoinit code for procedure
*/
if (var->Flags & TF_AUTOINIT) {
#ifdef REGISTERED
printf("\tsection\tautoinit1,code\n");
printf("\tjsr\t_%s\n", SymToString(var->Sym));
puts(LastSectBuf);
#else
yerror(var->LexIdx, EUNREG_AUTOINIT);
#endif
} else if (var->Flags & TF_AUTOEXIT) {
#ifdef REGISTERED
printf("\tsection\tautoexit1,code\n");
printf("\tjsr\t_%s\n", SymToString(var->Sym));
puts(LastSectBuf);
#else
yerror(var->LexIdx, EUNREG_AUTOEXIT);
#endif
}
/*
* autoinit/exit code for profiling
*
* Use address of label(s) after call to ID the routine(s)
*/
if (ProfOpt && (var->Flags & (TF_NOPROF|TF_INTERRUPT)) == 0) {
long l1 = AllocLabel();
LabelProfBeg = AllocLabel();
LabelProfEnd = AllocLabel();
AddAuxSub("ProfInit"); /* ProfInit(rtname, pcbeg, pcend) */
AddAuxSub("ProfExec"); /* ProfInit(rtname, pcbeg, pcend) */
printf("\tsection\tautoinit1,code\n");
printf("\tlea\tl%ld%s,A0\n", l1, (SmallData) ? "(A4)" : "");
printf("\tjsr\t__ProfInit\n");
printf("\tsection\tlibdata,data\n");
printf("\tds.l\t0\n");
printf("l%ld\n", l1);
printf("\tdc.l\t0\n"); /* next pointer */
printf("\tdc.l\t0\n"); /* siblings */
printf("\tdc.l\t0\n"); /* our parent */
printf("\tdc.w\t%d\n", (40 + strlen(prefix) + strlen(SymToString(var->Sym)) + (1 + 3)) & ~3); /* size of structure */
printf("\tdc.w\t0\n");
printf("\tdc.l\t0\n"); /* time stamp */
printf("\tdc.l\t0\n"); /* accum time */
printf("\tdc.l\t0\n"); /* total time */
printf("\tdc.l\t0\n"); /* # of calls */
printf("\tdc.l\tl%ld\n", LabelProfBeg);
printf("\tdc.l\tl%ld\n", LabelProfEnd);
printf("\tdc.b\t\'%s%s\',0\n", prefix, SymToString(var->Sym));
printf("\tds.l\t0\n");
puts(LastSectBuf);
}
if (GenLinkOpt || ((GenStackOpt || (var->Flags & TF_STKCHECK)) && !(var->Flags & (TF_INTERRUPT|TF_NOSTKCHECK))))
printf("\tprocstart\t0\n"); /* enable opts except link/unlk */
else
printf("\tprocstart\n"); /* enable optimizations */
printf("\tds.w\t0\n"); /* Align the code */
if (!(var->Flags & TF_STATIC))
printf("\txdef\t%s%s\n", prefix, SymToString(var->Sym));
printf("%s%s:\n", prefix, SymToString(var->Sym));
if (ProfOpt && (var->Flags & (TF_NOPROF|TF_INTERRUPT)) == 0) {
printf("\tjsr\t__ProfExec%s\n", (SmallCode) ? "(pc)" : "");
printf("l%ld\n", LabelProfBeg);
}
printf("\tmovem.l\tl%ld,-(sp)\n", LabelRegMaskUsed);
}
/*
* REGCALLORDER()
*
* Fill a register ordering array (ano) with the registers for a
* registerized procedure call.
*
* Returns a mask of registers that conflict with currently allocated
* registers. Would previously generate a conflict bit for byte sized
* address register quantities but no longer does so (has been fixed
* in GenRegArgs)
*/
long
RegCallOrder(type, ano, prgno)
Type *type;
char *ano;
char *prgno; /* prefilled pragma regs or NULL */
{
short i;
Var *var;
long regMask = 0;
for (i = 0; i < type->Args && (var = type->Vars[i]); ++i) {
short regNo = 0;
if (prgno && prgno[i] != -1) {
regNo = prgno[i];
} else if (var->RegFlags & RF_REGISTER) {
regNo = var->RegFlags & RF_REGMASK;
} else {
dbprintf(("RegFlags %d prgno %08lx prgno[i] %d\n",
var->RegFlags,
(long)prgno,
(prgno) ? prgno[i] : -1
));
Assert(0);
}
regMask |= RegCallUseRegister(regNo);
ano[i] = regNo;
#ifdef NOTDEF
if (regNo >= RB_ADDR && var->Type->Size == 1) {
dbprintf(("; RegCallOrder ADREG/CHAR %d\n", regNo));
regMask |= 1 << regNo;
}
#endif
}
if (prgno) {
short regNo;
while (i < 16 && (regNo = prgno[i]) != -1) {
ano[i] = regNo;
regMask |= RegCallUseRegister(regNo);
++i;
}
}
while (i < 16)
ano[i++] = -1;
return(regMask);
}
/*
* AUTOASSIGNREGISTEREDARGS()
*
* When automatic registerized variables are enabled this routine
* will assign procedure arguments to registers. This routine is
* also called for explicitly registerized procedure calls but
* in that case the registers are already assigned and we have an
* effective nop.
*/
void
AutoAssignRegisteredArgs(Var **vars, short args)
{
short i;
long regMask = 0;
Var *var;
for (i = 0; i < args && (var = vars[i]); ++i) {
Assert(i < 16);
if ((var->RegFlags & RF_REGISTER) == 0) {
long pick = 0;
if (var->Type->Id == TID_INT) {
if ((regMask & RF_D0) == 0)
pick = RB_D0;
else if ((regMask & RF_D1) == 0)
pick = RB_D1;
else if ((regMask & RF_A0) == 0)
pick = RB_A0;
else if ((regMask & RF_A1) == 0)
pick = RB_A1;
else
Assert(0);
} else {
if ((regMask & RF_A0) == 0)
pick = RB_A0;
else if ((regMask & RF_A1) == 0)
pick = RB_A1;
else if ((regMask & RF_D0) == 0)
pick = RB_D0;
else if ((regMask & RF_D1) == 0)
pick = RB_D1;
else
Assert(0);
}
var->RegFlags = pick | RF_REGISTER;
}
regMask |= 1 << (var->RegFlags & RF_REGMASK);
}
}
/*
* returns whether a register swap had to be done (so GEN.C can invalidate
* the variable as reservable since it has been effectively modified)
*/
long
asm_rcomove(Exp *exp, char *ano, short i, Stor *d)
{
Stor t;
short swap = -1;
short si;
t.st_Flags= SF_TMP;
t.st_Type = ST_Reg;
t.st_RegNo= ano[i];
t.st_Size = d->st_Size;
if (d->st_Type != ST_Reg) {
asm_move(exp, &t, d);
ano[i] = 0;
return(0);
}
if (d->st_RegNo != ano[i]) {
for (si = 0; si < 16; ++si) {
if (d->st_RegNo == ano[si]) {
swap = si;
break;
}
}
if (swap < 0) { /* destination contents garbage */
outop("move", 4, &t, d);
} else { /* destination contents precious */
outop("exg", 4, &t, d);
ano[si] = t.st_RegNo;
}
ano[i] = -1;
}
return(swap);
}
void
asm_exg(s1, s2)
Stor *s1;
Stor *s2;
{
if (SameStorage(s1, s2) == 0)
outop("exg", 4, s1, s2);
}
#ifdef NOTDEF
/*
* redefine the movem label.. make the old one nil (effecively move
* the movem to *after* the registers are loaded)
*/
void
asm_regentry(var)
Var *var;
{
char *nam = SymToString(var->Sym);
printf("l%ld\teqr\n", LabelRegMaskUsed);
printf("\txdef\t@%s\n", nam);
printf("@%s:\n", nam);
LabelRegMaskUsed= AllocLabel();
printf("\tmovem.l\tl%ld,-(sp)\n", LabelRegMaskUsed);
}
#endif
void
asm_proclink(var)
Var *var;
{
if (var->Flags & (TF_INTERRUPT|TF_GETA4)) {
if (var->Flags & TF_GETA4) {
#ifdef REGISTERED
RegReserved |= REGSDPTR; /* XXX remove me */
AddAuxSub("ABSOLUTE_BAS");
if (PIOpt) {
printf("\tlea\t__ABSOLUTE_BAS(pc),A4\n"
"\tlea\t32766(A4),A4\n"
);
} else {
printf("\tlea\t__ABSOLUTE_BAS+32766,A4\n");
if (ResOpt)
yerror(var->LexIdx, EERROR_GETA4_ILLEGAL);
}
#else
yerror(var->LexIdx, EUNREG_GETA4);
#endif
}
/* int code? */
}
/*
* If -gs is specified do stack checking, otherwise just do a simple
* link instruction.
*
* In order to do stack checking we have to simulate the link
* instruction and modify A5 instead of SP, then check A6, then
* EXG the two if we are still ok. We cannot store the link
* equivalent into A7 until we know it is safe.
*/
if ((GenStackOpt || (var->Flags & TF_STKCHECK)) && !(var->Flags & (TF_INTERRUPT|TF_NOSTKCHECK))) {
long l = AllocLabel();
AddAuxSub("stk_base");
AddAuxSub("stk_alloc");
/*
* simulate link instruction but with A5 and A7 swapped
*/
printf("\tmove.l\tA%d,-(sp)\n", RB_FP - RB_ADDR);
printf("\tlea\t-l%ld(sp),A%d\n", LabelStackUsed, RB_FP - RB_ADDR);
printf("\tcmp.l\t__stk_base%s,A%d\n",
((SmallData) ? "(A4)" : ""),
RB_FP - RB_ADDR
);
printf("\tbhi\tl%ld\n", l);
/*
* Call __stk_alloc() (name changed from __stack_alloc() to avoid
* incompatible library functions after dynamic stacks were fixed).
* __stk_alloc() requires the number of bytes of arguments and
* the number of bytes of saved registers in order to be able to
* properly allocate a new stack frame and copy the appropriate
* information.
*
* __stk_alloc() will replace the return vector and saved frame
* pointer in the new frame to point to __stk_free(), which
* restores the old frame.
*
* NOTE: The minimum argument size for K&R procedures is 256 bytes
* more then the specified arguments in order to accomodate
* var-args functions. ANSI prototyped procedures are much more
* efficient, only copying the actual number of arguments, plus
* 256 more bytes if the (, ...) form is used.
*/
{
BlockStmt *block = var->u.Block;
long argBytes = block->Frame.ArgsStackUsed;
if (var->Flags & TF_PROTOTYPE) {
if (var->Flags & TF_DOTDOTDOT)
argBytes += 256;
} else {
argBytes += 256;
}
if (argBytes < 32768)
printf("\tpea\t%ld.W\n", argBytes);
else
printf("\tpea\t%ld\n", argBytes);
}
printf("\tpea\tl%ld-8.W\n", LabelRegsUsed);
printf("\tjsr\t__stk_alloc%s\n", (SmallCode) ? "(pc)" : "");
/*
* exg A5, A7 to 'make it right'
*/
printf("l%ld\texg\tsp,A%d\n", l, RB_FP - RB_ADDR);
} else {
printf("\tlink\tA%d,#-l%ld\n", RB_FP - RB_ADDR, LabelStackUsed);
}
}
void
asm_procend(Var *var, short forceLink)
{
BlockStmt *block = var->u.Block;
short count;
short dummy;
#ifdef MINIDICE
static short Cntr = (MINIMAXPROCS + 2) * 3;
#endif
long stack = Align(block->Frame.DownStackUsed + block->Frame.StackUsed, STACK_ALIGN);
long mask = GetUsedRegisters() & ~RegReserved;
if (var->Flags & (TF_INTERRUPT)) {
if (SmallData != 2)
mask |= REGSDPTR;
if (block->Frame.Flags & FF_CALLMADE)
mask |= REGSCRATCH;
} else {
mask &= ~REGSCRATCH;
}
if (var->Flags & TF_GETA4)
mask |= REGSDPTR;
#ifdef MINIDICE
else
Cntr -= 3;
#endif
if (stack > 32767)
yerror(var->LexIdx, EERROR_TOO_MANY_AUTOS);
#ifdef MINIDICE
if (Cntr <= 0) {
GlobalRegReserved |= 1 << (var->LexIdx & 31);
dbprintf(("Holy shit batman! I've been compromised %d\n", (var->LexIdx & 31)));
}
#endif
/*
* return, unlk, restore regs, rts.
*/
asm_label(LabelReturn);
printf("\tunlk\tA%d\n", RB_FP - RB_ADDR);
count = asm_restore_regs(mask);
if (var->Flags & TF_INTERRUPT) {
printf("\trte\n");
} else {
if (ProfOpt && (var->Flags & TF_NOPROF) == 0) {
printf("\tjsr\t__ProfExec%s\n", (SmallCode) ? "(pc)" : "");
printf("l%ld\n", LabelProfEnd);
}
printf("\trts\n");
}
printf("l%ld\treg\t%s\n", LabelRegMaskUsed, RegMaskToString(mask, &dummy));
printf("l%ld\tequ\t%d\n", LabelRegsUsed, 8 + count * 4);
printf("l%ld\tequ\t%ld\n", LabelStackUsed, stack);
printf("\tprocend");
if (forceLink)
printf(" forcelink");
puts("");
}
void
asm_push_mask(mask)
long mask;
{
if (mask) {
short n;
char *ptr = RegMaskToString(mask, &n);
printf("\tmove%s.l\t%s,-(sp)\n", ((n > 1) ? "m" : ""), ptr);
}
}
void
asm_pop_mask(mask)
long mask;
{
if (mask) {
short n;
char *ptr = RegMaskToString(mask, &n);
printf("\tmove%s.l\t(sp)+,%s\n", ((n > 1) ? "m" : ""), ptr);
}
}
void
asm_returnstorage(exp)
Exp *exp;
{
Stor sd0;
Stor sd1;
Stor sa0;
Stor sa1;
Stor *s = &exp->ex_Stor;
switch(exp->ex_Type->Id) {
case TID_STRUCT:
case TID_UNION:
{
int label = AllocLabel();
/*
* structures and unions are returned to storage allocated by
* the caller. The caller passes a pointer to this storage
* as the first argument in the call, which is -4 relative
* ST_RelArg/fp (asubs.c will modify the basic RelArg offset
* so 0 always points to the first user arguments)
*
* Note that if the caller expects no return data it will
* pass NULL for the pointer, in which case we do NOT
* copy the return structure.
*/
/*
* used to allocate A0 but this is illegal -- A0 might already
* be used to hold the source of the copy.
*/
AllocAddrRegister(&sa0);
SCallerBlk.st_Offset = -4;
asm_move(exp, &SCallerBlk, &sa0);
asm_test(exp, &sa0);
asm_condbra(COND_EQ, label);
sa0.st_Offset = 0;
sa0.st_Type = ST_RelReg;
sa0.st_Size = s->st_Size;
asm_move(exp, s, &sa0);
FreeRegister(&sa0);
asm_label(label);
}
break;
case TID_FLT:
AllocDataRegisterAbs(&sd0, 4, RB_D0);
AllocDataRegisterAbs(&sd1, 4, RB_D1);
if (s->st_Type == ST_FltConst) {
ulong ary[4];
asm_fltconst(exp, s, ary);
switch(s->st_Size) {
case 16:
AllocAddrRegisterAbs(&sa0, RB_A0);
AllocAddrRegisterAbs(&sa1, RB_A1);
asm_movei(exp, ary[3], &sa1);
asm_movei(exp, ary[2], &sa0);
FreeRegister(&sa0);
FreeRegister(&sa1);
case 8:
asm_movei(exp, ary[1], &sd1);
case 4:
asm_movei(exp, ary[0], &sd0);
break;
default:
Assert(0);
}
} else {
char *sstr = StorToString(s, NULL);
switch(s->st_Size) {
case 4:
printf("\tmove.l\t%s,D0\n", sstr);
break;
case 8:
printf("\tmovem.l\t%s,D0/D1\n", sstr);
break;
case 16:
AllocAddrRegisterAbs(&sa0, RB_A0);
AllocAddrRegisterAbs(&sa1, RB_A1);
printf("\tmovem.l\t%s,D0/D1/A0/A1\n", sstr);
FreeRegister(&sa0);
FreeRegister(&sa1);
break;
default:
Assert(0);
}
}
FreeRegister(&sd0);
FreeRegister(&sd1);
break;
case TID_INT: /* size 4 D0 */
case TID_PTR:
Assert(s->st_Size == 4);
AllocDataRegisterAbs(&sd0, 4, RB_D0);
asm_move(exp, s, &sd0);
FreeRegister(&sd0);
break;
default:
yerror(exp->ex_LexIdx, EERROR_UNSUPPORTED_RETURN_TYPE);
break;
}
}
#ifdef DYNAMIC
/*
* geneate dynamic tag code, variable is a pointer to the procedure
* or variable we dynamically link in run-time.
*
* procedure: _GetHyperFunc("routine", "_stk_")
* variable: _GetHyperVar("variable")
*
*/
void
asm_dynamictag(var)
Var *var;
{
long l1 = AllocLabel();
long l2 = AllocLabel();
long l3 = AllocLabel();
short isVar = (var->Type->SubType->Id != TID_PROC);
/*
* autoinit code
*/
printf("\tsection\tautoinit1,code\n");
printf("\txref\t__AutoFail1\n");
printf("\txref\t@_%s\n",
((isVar) ? "GetHyperVar" : "GetHyperFunc")
);
if (isVar == 0)
printf("\tlea\tl%d(pc),A1\n", l2);
printf("\tlea\tl%d(pc),A0\n", l1);
printf("\tjsr\t@_%s%s\n",
((isVar) ? "GetHyperVar" : "GetHyperFunc"),
((PIOpt) ? "(pc)" : "")
);
printf("\tmove.l\tD0,_%s%s\n",
SymToString(var->Sym),
((SmallData) ? "(A4)" : "")
);
printf("\tbeq\t__AutoFail1\n");
printf("\tbra\tl%d\n", l3);
printf("l%d\tdc.b\t'_%s',0\n", l1, SymToString(var->Sym));
if (isVar == 0)
printf("l%d\tdc.b\t'_stk_',0\n", l2);
printf("\tds.w\t0\n");
printf("l%d\n", l3);
/*
* autoexit code to release dynamically referenced variable or routine
*/
printf("\tsection\tautoexit1,code\n");
printf("\txref\t@_%s\n",
((isVar) ? "RelsHyperVar" : "RelsHyperFunc")
);
printf("\tmove.l\t_%s%s,A0\n",
SymToString(var->Sym),
((SmallData) ? "(A4)" : "")
);
printf("\tjsr\t@_%s%s\n",
((isVar) ? "RelsHyperVar" : "RelsHyperFunc"),
((PIOpt) ? "(pc)" : "")
);
puts(LastSectBuf);
}
#endif
void
asm_segment(var)
Var *var;
{
long mask;
long state;
char *segName;
char *segType;
char *sn;
#ifdef REGISTERED
if (var->Flags & TF_CHIP)
mask = 0x40000000;
else
mask = 0;
#else
mask = 0;
if (var->Flags & TF_CHIP)
yerror(var->LexIdx, EUNREG_CHIP);
#endif
if (var->var_Stor.st_Flags & SF_CODE) {
if (var->Flags & TF_AUTOINIT) { /* __autoinit const only */
segName = SegNames[7];
segType = SegTypes[7];
} else {
segName = SegNames[0];
segType = SegTypes[0];
}
state = ASM_CODE;
} else {
long idx;
if (var->u.AssExp)
state = ASM_DATA;
else
state = ASM_BSS;
if (var->Flags & TF_AUTOINIT) {
if (var->u.AssExp)
idx = 5;
else
idx = 6;
} else if ((var->Flags & TF_FAR) || (SmallData == 0 && !(var->Flags & TF_NEAR))) {
if (var->u.AssExp)
idx = 2;
else if (UnixCommonOpt && (var->Flags & TF_STATIC) == 0)
idx = 9;
else
idx = 4;
} else {
if (var->u.AssExp)
idx = 1;
else if (UnixCommonOpt && (var->Flags & TF_STATIC) == 0)
idx = 8;
else
idx = 3;
}
segName = SegNames[idx];
segType = SegTypes[idx];
}
state |= mask;
sn = LastSectBuf;
if (AsmState != state || LastSectName != segName) {
if (mask)
sprintf(sn, "\tsection %s,%s,$%08lx\n", segName, segType, mask);
else
sprintf(sn, "\tsection %s,%s\n", segName, segType);
puts(sn);
AsmState = state;
LastSectName = segName;
OutAlign = 1;
}
}
void
asm_align(long size)
{
switch(size) {
case 1:
break;
case 2:
if (OutAlign & 1)
printf("\tds.w 0\n");
OutAlign = 2;
break;
case 4:
default:
if (OutAlign & 3)
printf("\tds.l 0\n");
OutAlign = 4;
break;
}
}
/*
* Generate a string ref. An internationalizable reference generates a
* table reference (and the string is forced to be in a code section elsewhere
* in the code)
*/
void
asm_string(label, str, bytes, flags, iidx)
long label;
ubyte *str;
long bytes;
long flags;
long iidx;
{
short i = 0;
char buf[128];
char *p = buf;
#ifdef COMMERCIAL
if (iidx >= 0) {
long newLabel = AllocLabel();
printf("\tsection\tilocale,data\n");
printf("l%ld\tdc.l\tl%ld,%ld\n", label, newLabel, iidx);
puts(LastSectBuf);
label = newLabel;
}
#endif
if (flags & (TF_CONST | TF_SHARED))
asm_segment(&DummyCodeVar);
else
asm_segment(&DummyDataVar);
printf("\tds.w\t0\n"); /* Word align the string */
*p++ = 'l';
p = itodec(p, label);
*p++ = '\n';
while (bytes--) {
if (i) {
*p++ = ',';
} else {
strcpy(p, "\n\tdc.b\t");
p += 7;
}
*p++ = '$';
p = itohex(p, *str++);
if (++i == 12) {
fwrite(buf, p - buf, 1, stdout);
p = buf;
i = 0;
}
}
*p++ = '\n';
fwrite(buf, p - buf, 1, stdout);
}
void
asm_label(label)
long label;
{
char buf[32];
char *ptr;
buf[0] = 'l';
ptr = itodec(buf + 1, label);
*ptr = '\n';
++ptr;
fwrite(buf, 1, ptr - buf, stdout);
}
/*
* BRANCHING. NOTE NOTE NOTE. Whenever we branch we must check
* for forced registers which must be restored.
*/
void
asm_branch(label)
long label;
{
printf("\tbra\tl%ld\n", label);
}
void
asm_condbra(short cond, long label)
{
char *str = "bad";
switch(cond) {
case COND_LT:
str = "blt";
break;
case COND_LTEQ:
str = "ble";
break;
case COND_GT:
str = "bgt";
break;
case COND_GTEQ:
str = "bge";
break;
case COND_EQ:
str = "beq";
break;
case COND_NEQ:
str = "bne";
break;
case CF_UNS|COND_LT:
str = "bcs";
break;
case CF_UNS|COND_LTEQ:
str = "bls";
break;
case CF_UNS|COND_GT:
str = "bhi";
break;
case CF_UNS|COND_GTEQ:
str = "bcc";
break;
case CF_UNS|COND_EQ:
str = "beq";
break;
case CF_UNS|COND_NEQ:
str = "bne";
break;
case -COND_LT:
str = "bge";
break;
case -COND_LTEQ:
str = "bgt";
break;
case -COND_GT:
str = "ble";
break;
case -COND_GTEQ:
str = "blt";
break;
case -COND_EQ:
str = "bne";
break;
case -COND_NEQ:
str = "beq";
break;
case -(CF_UNS|COND_LT):
str = "bcc";
break;
case -(CF_UNS|COND_LTEQ):
str = "bhi";
break;
case -(CF_UNS|COND_GT):
str = "bls";
break;
case -(CF_UNS|COND_GTEQ):
str = "bcs";
break;
case -(CF_UNS|COND_EQ):
str = "bne";
break;
case -(CF_UNS|COND_NEQ):
str = "beq";
break;
case COND_BPL:
str = "bpl";
break;
case COND_BMI:
str = "bmi";
break;
default:
dbprintf(("Unknown cond %d\n", cond));
Assert(0);
break;
}
printf("\t%s\tl%ld\n", str, label);
}
/*
* push args onto stack, return bytes pushed.
*/
long
asm_push(exp, type, s)
Exp *exp;
Type *type;
Stor *s;
{
Stor t;
if (s->st_Type == ST_IntConst) {
if (s->st_IntConst >= -32768 && s->st_IntConst < 32768) {
if (s->st_IntConst == 0)
printf("\tclr.l\t-(sp)\n");
else
printf("\tpea\t%ld.W\n", s->st_IntConst);
} else {
outop("move", 4, s, &SPush);
}
return(4);
}
if (s->st_Type == ST_FltConst) {
long fval[4];
short i;
asm_fltconst(exp, s, fval);
for (i = (s->st_Size >> 2) - 1; i >= 0; --i) {
long v = fval[i];
if (v >= -32768 && v < 32768)
printf("\tpea\t$%08lx.W\n", v);
else
printf("\tpea\t$%08lx\n", v);
}
return(s->st_Size);
}
/*
* note, IntConst case already handled.
*/
if (type->Id == TID_ARY || type->Id == TID_PROC || (s->st_Flags & SF_LEA)) {
outop("pea", 4, NULL, s);
return(4);
}
/*
* pointer, long, 4 byte structure
*/
if (s->st_Size == 4) {
outop("move", 4, s, &SPush);
return(4);
}
/*
* structure or fp number, guarenteed to be word aligned. Push
* exact size.
*/
if (type->Id != TID_INT) {
long bytes = s->st_Size;
if (s->st_Size < 4) {
outop("move", s->st_Size, s, &SPush);
return(s->st_Size);
}
if ((s->st_Type < ST_RelReg || s->st_Type > ST_RegIndex) && s->st_Type != ST_PtrConst)
{
dbprintf(("bad type in asm_push: %d\n", s->st_Type));
Assert(0);
}
if (bytes <= LGBO_SIZE && s->st_Offset >= -32768 + LGBO_SIZE && s->st_Offset < 32767 - LGBO_SIZE) {
long oldOffset = s->st_Offset;
s->st_Offset += bytes;
while (bytes >= 4) {
bytes -= 4;
s->st_Offset -= 4;
outop("move", 4, s, &SPush);
}
s->st_Offset = oldOffset;
if (bytes)
outop("move", bytes, s, &SPush);
} else {
long label = AllocLabel();
Stor a;
if (bytes & 1)
yerror(exp->ex_LexIdx, EERROR_PASS_UNALIGNED_STRUCT);
LockStorage(s);
AllocDataRegister(&t, 4);
AllocAddrRegister(&a);
asm_lea(exp, s, bytes, &a); /* end of struct */
asm_movei(exp, bytes >> 2, &t); /* # of longwords */
if (bytes & 2)
printf("\tmove.w\t-(A%c),-(sp)\n", a.st_RegNo - RB_ADDR + '0');
asm_label(label);
printf("\tmove.l\t-(A%c),-(sp)\n", a.st_RegNo - RB_ADDR + '0');
printf("\tsubq.l\t#1,%s\n", StorToString(&t, NULL));
asm_condbra(COND_NEQ, label);
FreeRegister(&t);
FreeRegister(&a);
UnlockStorage(s);
}
return(s->st_Size);
}
/*
* < sizeof(int), optimizeo. push an entire longword quantity even if
* the upper bits would be garbage.
*/
Assert(s->st_Size < 4);
if (s->st_Type == ST_Reg) {
outop("move", 4, s, &SPush);
return(4);
}
/*
* safe to push extranious garbage (assuming offset lw aligned)
*/
if (s->st_Type == ST_RelArg || (s->st_RegNo == RB_FP && s->st_Type == ST_RelReg)) {
long offset = s->st_Offset + s->st_Size - 4;
if ((offset & 1) == 0) {
s->st_Offset = offset;
outop("move", 4, s, &SPush);
s->st_Offset -= s->st_Size - 4;
return(4);
}
}
/*
* note safe to push extranious garbage from before lvalue
*/
switch(s->st_Size) {
case 1:
printf("\tsubq.l\t#4,sp\n");
SOff.st_Offset = 3;
outop("move", 1, s, &SOff);
break;
case 2:
outop("move", 2, s, &SPush);
printf("\tsubq.l\t#2,sp\n");
break;
default:
dbprintf(("unexpected push size %ld\n", s->st_Size));
Assert(0);
}
return(4);
}
/*
* call s placing the return value into d. d can be NULL
*
* Note that d might also have been part of the registers saved,
* so we have to restore before moving the result. Except D0 might
* have been part of the registers saved as well so in this special
* case we must move D0 into a temporary register before restoring.
*/
long
asm_stackbytes(rtype)
Type *rtype;
{
long rtbytes = Align(rtype->Size, STACK_ALIGN);
if (rtbytes > 0 && rtbytes < 4)
rtbytes = 4;
return(rtbytes);
}
/*
* asm_call() make subroutine call
*
* autopush: 0x01 push result back onto stack
* 0x02 regargs call
*
* note that autopush & 1 is never set for structural return types
*/
void
asm_call(Exp *exp, Stor *s, Type *rtype, Stor *d, long bytes, short autopush)
{
long rtbytes;
if (rtype->Id == TID_STRUCT || rtype->Id == TID_UNION) {
Stor t;
if (d) {
asm_getlea(exp, d, &t);
asm_push(exp, &VoidPtrType, &t);
FreeStorage(&t);
} else {
AllocConstStor(&t, 0, &LongType);
asm_push(exp, &VoidPtrType, &t);
}
bytes += 4;
}
outop("jsr", -1, NULL, s);
if (autopush & 1) {
char *spnam;
rtbytes = asm_stackbytes(rtype);
if (bytes == rtbytes) {
spnam = "(sp)";
} else {
spnam = "-(sp)";
asm_pop(bytes);
}
switch(rtbytes) {
case 4:
printf("\tmove.l\tD0,");
break;
case 8:
printf("\tmovem.l\tD0/D1,");
break;
case 16:
printf("\tmovem.l\tD0/D1/A0/A1,");
break;
default:
yerror(exp->ex_LexIdx, EERROR_UNSUPPORTED_RETURN_TYPE);
break;
}
puts(spnam);
} else {
asm_pop(bytes);
}
if (d) {
Stor sd0;
Stor sd1;
Stor sa0; /* long double D0/D1/A0/A1 */
Stor sa1;
switch(rtype->Id) {
case TID_STRUCT:
case TID_UNION:
/*
* structural return storage is written directly to the
* destination by the caller, so we need do nothing more.
*/
break;
case TID_FLT:
if (d->st_Size > 4) {
AllocDataRegisterAbs(&sd0, d->st_Size, RB_D0);
AllocDataRegisterAbs(&sd1, d->st_Size, RB_D1);
switch(d->st_Size) {
case 8:
printf("\tmovem.l\tD0/D1,%s\n", StorToString(d, NULL));
break;
case 16:
AllocAddrRegisterAbs(&sa0, RB_A0);
AllocAddrRegisterAbs(&sa1, RB_A1);
printf("\tmovem.l\tD0/D1/A0/A1,%s\n", StorToString(d, NULL));
FreeRegister(&sa0);
FreeRegister(&sa1);
break;
default:
Assert(0);
}
FreeRegister(&sd0);
FreeRegister(&sd1);
break;
}
/* fall through single prec fp */
case TID_INT:
case TID_PTR:
AllocDataRegisterAbs(&sd0, d->st_Size, RB_D0);
asm_move(exp, &sd0, d);
FreeRegister(&sd0);
break;
default:
yerror(exp->ex_LexIdx, EERROR_UNSUPPORTED_RETURN_TYPE);
break;
}
}
}
void
asm_pop(n)
long n;
{
if (n) {
if (n <= 8) {
printf("\taddq.l\t#%ld,sp\n", n);
return;
}
if (n < 32768) {
printf("\tlea\t%ld(sp),sp\n", n);
return;
}
printf("\taddi.l\t#%ld,sp\n", n);
}
}
/*
* signed/unsigned extend w/ major optimizeo. Also handles truncation
*
* NOTE: case with data register as destination assumed never to
* allocate temporary registers (by asm_div, because we use D0/D1
* without allocating them).
*/
void
asm_ext(exp, s, d, sflags)
Exp *exp;
Stor *s, *d;
long sflags;
{
Stor stor;
Stor stor2;
long saveDSize = d->st_Size;
sflags &= SF_UNSIGNED;
if (s->st_Type == ST_IntConst) {
long v = s->st_IntConst;
if (d->st_Size == 2)
v &= 0xFFFF;
if (d->st_Size == 1)
v &= 0xFF;
asm_movei(exp, v, d);
return;
}
/*
* If source is an address register cannot use .b size. Must move into
* data register. However, if destination is .b sized and also a
* register, we can simply move it to the destination and be done
* with it.
*/
if (d->st_Size == 1 && s->st_Type == ST_Reg && s->st_RegNo >= RB_ADDR) {
/*
* If register-register we don't care if we use more of the
* destination register and this saves having to move the
* address register into a temporary
*/
if (d->st_Type == ST_Reg) {
long saveSSize = s->st_Size;
d->st_Size = s->st_Size = 2;
asm_move(exp, s, d);
s->st_Size = saveSSize;
d->st_Size = saveDSize;
return;
}
/*
* s is an address register, destination size is 1. Use .w move
* since can't use .b move. Since the destination is not a
* register we have to allocate one.
*/
AllocDataRegister(&stor2, 2);
s->st_Size = 2;
asm_move(exp, s, &stor2);
s->st_Size = 1;
stor2.st_Size = 1;
FreeRegister(&stor2);
s = &stor2;
}
/*
* If destination is address register cannot use .b size. Easy
* fix is to temporarily force destination size to 2.
*/
if (d->st_Size == 1 && d->st_Type == ST_Reg && d->st_RegNo >= RB_ADDR)
d->st_Size = 2;
if (s->st_Size > d->st_Size) {
stor = *s;
switch(stor.st_Type) {
case ST_PtrConst:
case ST_RelArg:
case ST_RelReg:
case ST_RegIndex:
case ST_RelLabel:
case ST_RelName:
stor.st_Offset += s->st_Size - d->st_Size;
break;
}
stor.st_Size = d->st_Size;
asm_move(exp, &stor, d);
} else if (s->st_Size == d->st_Size) {
asm_move(exp, s, d);
} else if (SameStorage(s, d) && s->st_Type == ST_Reg) {
if (s->st_RegNo >= RB_ADDR)
{
dbprintf(("asm_ext : ext An")); /* An */
Assert(0);
}
if (sflags) {
AllocConstStor(&stor, (1 << 8*s->st_Size) - 1, &LongType);
stor.st_Size = d->st_Size;
outop("andi", d->st_Size, &stor, d);
} else {
extop(s->st_Size, d->st_Size, d);
}
} else if (d->st_Type == ST_Reg && d->st_RegNo < RB_ADDR && !SameRegister(s, d)) {
if (sflags) {
long size = d->st_Size;
LockStorage(s);
asm_movei(exp, 0, d);
UnlockStorage(s);
d->st_Size = s->st_Size;
asm_move(exp, s, d);
d->st_Size = size;
} else {
long size = d->st_Size;
d->st_Size = s->st_Size;
asm_move(exp, s, d);
d->st_Size = size;
extop(s->st_Size, d->st_Size, d);
}
} else {
if (sflags) {
LockStorage(s);
AllocDataRegister(&stor, 4);
asm_movei(exp, 0, &stor);
UnlockStorage(s);
stor.st_Size = s->st_Size;
asm_move(exp, s, &stor);
stor.st_Size = d->st_Size;
asm_move(exp, &stor, d);
FreeRegister(&stor);
} else {
AllocDataRegister(&stor, s->st_Size);
asm_move(exp, s, &stor);
stor.st_Size = d->st_Size;
extop(s->st_Size, d->st_Size, &stor);
asm_move(exp, &stor, d);
FreeRegister(&stor);
}
}
d->st_Size = saveDSize;
}
void
extop(size1, size2, d)
long size1;
long size2;
Stor *d;
{
if (size1 == 1 && size2 > 2) {
if (MC68020Opt) {
outop("extb", size2, NULL, d);
return;
}
outop("ext", 2, NULL, d);
}
outop("ext", size2, NULL, d);
}
void
asm_test(exp, s)
Exp *exp;
Stor *s;
{
if (s->st_Flags & SF_BITFIELD) {
asm_bftst(exp, s);
return;
}
if (s->st_Type == ST_IntConst)
{
dbprintf(("asm_test: integer constant %ld", s->st_IntConst));
Assert(0);
}
if ((s->st_Type == ST_Reg && s->st_RegNo >= RB_ADDR) || (s->st_Flags & SF_LEA) || ((s->st_Type == ST_RelLabel || s->st_Type == ST_RelName) && (s->st_Flags & SF_CODE))) {
Stor stor;
LockStorage(s);
AllocDataRegister(&stor, s->st_Size);
UnlockStorage(s);
asm_move(exp, s, &stor);
FreeRegister(&stor);
return;
}
outop("tst", 0, NULL, s);
}
/*
* note: fp calls destroy scratch regs, so I have changed the moveq 0/scc
* to scc/ext
*/
void
asm_test_scc(Exp *exp, long typeid, Stor *s, short cond, Stor *d)
{
short isfp = (typeid == TID_FLT) ? 1 : 0;
if (SameStorage(s, d) || SameRegister(s, d) || d->st_Type != ST_Reg || d->st_RegNo >= RB_ADDR) {
Stor t;
LockStorage(s);
AllocDataRegister(&t, d->st_Size);
UnlockStorage(s);
if (isfp) {
asm_fptest(exp, s);
} else {
if (d->st_Size != 1)
asm_movei(exp, 0, &t);
asm_test(exp, s);
}
asm_sccb(exp, &t, cond, 0);
if (isfp)
extop(1, d->st_Size, &t);
FreeRegister(&t);
asm_move(exp, &t, d);
} else {
if (isfp) {
asm_fptest(exp, s);
} else {
if (d->st_Size != 1)
asm_movei(exp, 0, d);
asm_test(exp, s);
}
asm_sccb(exp, d, cond, 0);
if (isfp)
extop(1, d->st_Size, d);
}
}
void
asm_cond_scc(exp, typeid, s1, s2, pcond, d)
Exp *exp;
long typeid;
Stor *s1;
Stor *s2;
short *pcond;
Stor *d;
{
short isfp = (typeid == TID_FLT) ? 1 : 0;
if (SameStorage(s1, d) || SameStorage(s2, d) || d->st_Type != ST_Reg || d->st_RegNo >= RB_ADDR) {
Stor t;
LockStorage(s1);
LockStorage(s2);
AllocDataRegister(&t, d->st_Size);
UnlockStorage(s1);
UnlockStorage(s2);
if (isfp) {
asm_fpcmp(exp, s1, s2, pcond); /* if cmp reverses args it negates cond */
} else {
if (d->st_Size != 1)
asm_movei(exp, 0, &t);
asm_cmp(exp, s1, s2, pcond); /* if cmp reverses args it negates cond */
}
asm_sccb(exp, &t, *pcond, 0);
if (isfp)
extop(1, d->st_Size, &t);
FreeRegister(&t);
asm_move(exp, &t, d);
} else {
if (isfp) {
asm_fpcmp(exp, s1, s2, pcond); /* if cmp reverses args it negates cond */
} else {
if (d->st_Size != 1)
asm_movei(exp, 0, d);
asm_cmp(exp, s1, s2, pcond); /* if cmp reverses args it negates cond */
}
asm_sccb(exp, d, *pcond, 0);
if (isfp)
extop(1, d->st_Size, d);
}
}
void
asm_sccb(Exp *exp, Stor *d, short cond, short negative)
{
char *str = "bad";
long size;
switch(cond) {
case COND_LT:
str = "slt";
break;
case COND_LTEQ:
str = "sle";
break;
case COND_GT:
str = "sgt";
break;
case COND_GTEQ:
str = "sge";
break;
case COND_EQ:
str = "seq";
break;
case COND_NEQ:
str = "sne";
break;
case CF_UNS|COND_LT:
str = "scs";
break;
case CF_UNS|COND_LTEQ:
str = "sls";
break;
case CF_UNS|COND_GT:
str = "shi";
break;
case CF_UNS|COND_GTEQ:
str = "scc";
break;
case CF_UNS|COND_EQ:
str = "seq";
break;
case CF_UNS|COND_NEQ:
str = "sne";
break;
case -COND_LT:
str = "sge";
break;
case -COND_LTEQ:
str = "sgt";
break;
case -COND_GT:
str = "sle";
break;
case -COND_GTEQ:
str = "slt";
break;
case -COND_EQ:
str = "sne";
break;
case -COND_NEQ:
str = "seq";
break;
case -(CF_UNS|COND_LT):
str = "scc";
break;
case -(CF_UNS|COND_LTEQ):
str = "shi";
break;
case -(CF_UNS|COND_GT):
str = "sls";
break;
case -(CF_UNS|COND_GTEQ):
str = "scs";
break;
case -(CF_UNS|COND_EQ):
str = "sne";
break;
case -(CF_UNS|COND_NEQ):
str = "seq";
break;
case COND_BPL:
str = "spl";
break;
case COND_BMI:
str = "smi";
break;
default:
dbprintf(("Unknown cond %d\n", cond));
Assert(0);
}
size = d->st_Size;
d->st_Size = 1;
outop(str, 0, NULL, d); /* result 0 or -1.B */
if (negative == 0) /* want positive r. */
outop("neg", 1, NULL, d);
d->st_Size = size;
}
void
asm_clr(exp, d)
Exp *exp;
Stor *d;
{
asm_movei(exp, 0, d);
}
void
asm_movei(exp, val, d)
Exp *exp;
long val;
Stor *d;
{
Stor stor;
AllocConstStor(&stor, val, &LongType);
stor.st_Size = d->st_Size;
asm_move(exp, &stor, d);
}
/*
*
void
asm_globlea(s, d)
Stor *s;
Stor *d;
{
Assert(d->st_Type == ST_RelReg);
printf("\tlea\t%s,A%c\n", StorToString(s, NULL), d->st_RegNo + '0' - RB_ADDR);
}
*
*/
/*
* Arithmatic
*/
void
asm_neg(exp, s, d)
Exp *exp;
Stor *s;
Stor *d;
{
Stor stor;
Assert(s->st_Size == d->st_Size);
if (SameStorage(s, d)) {
outop("neg", 0, NULL, d);
return;
}
AllocDataRegister(&stor, d->st_Size);
asm_move(exp, s, &stor);
outop("neg", 0, NULL, &stor);
FreeRegister(&stor);
asm_move(exp, &stor, d);
}
/*
* Attempt to optimize a 3-op by moving one source to the destination and then
* doing the operation with the other source to the destination. Only do this
* if the destination is a register (else if it were memory it would result in
* longer instructions OR screw ups if the memory is an IO location)
*
* becareful when the destination is an address register and s2 indirects
* through that self-same register!
*/
void
ThreeOp(exp, s1, s2, d, op)
Exp *exp;
Stor *s1;
Stor *s2;
Stor *d;
void (*op)(Exp *, Stor *, Stor *, Stor *);
{
if (d->st_Type == ST_Reg && !(d->st_RegNo == s2->st_RegNo && s2->st_Type == ST_RelReg)) {
LockStorage(s2);
asm_move(exp, s1, d);
UnlockStorage(s2);
(*op)(exp, d, s2, d);
} else {
Stor tmp;
LockStorage(s2);
AllocDataRegister(&tmp, s1->st_Size);
asm_move(exp, s1, &tmp);
UnlockStorage(s2);
(*op)(exp, &tmp, s2, &tmp);
asm_move(exp, &tmp, d);
FreeRegister(&tmp);
}
}
void
asm_add(exp, s1, s2, d)
Exp *exp;
Stor *s1;
Stor *s2;
Stor *d;
{
if (ImmStorage(s2)) {
SWAPS(s1, s2);
}
if (SameStorage(s1,d)) {
SWAPS(s1, s2);
}
if (!SameStorage(s2,d)) { /* 3 operand inst */
ThreeOp(exp, s1, s2, d, asm_add);
return;
}
if (s1->st_Flags & SF_LEA) {
Stor t;
LockStorage(s2);
AllocAddrRegister(&t);
asm_move(exp, s1, &t);
UnlockStorage(s2);
asm_add(exp, &t, s2, d);
FreeRegister(&t);
return;
}
if (d->st_Flags & SF_LEA)
yerror(exp->ex_LexIdx, EFATAL_DEST_NOT_LVALUE);
if (ImmStorage(s1)) {
if (s1->st_Type != ST_IntConst) { /* #label */
outop("add", 0, s1, d);
return;
} else {
if (s1->st_IntConst == 0) {
if (!SameStorage(s2,d))
asm_move(exp, s2, d);
return;
}
if (s1->st_IntConst >= -8 && s1->st_IntConst <= 8) {
if (s1->st_IntConst > 0) {
outop("addq", 0, s1, d);
} else {
s1->st_IntConst = -s1->st_IntConst;
outop("subq", 0, s1, d);
s1->st_IntConst = -s1->st_IntConst;
}
} else if (d->st_Type == ST_Reg && d->st_RegNo >= RB_ADDR) {
outop("adda", SizeFit(s1->st_IntConst), s1, d);
} else {
/*
* use moveq/add if possible
*/
if (d->st_Size == 4 && s1->st_IntConst >= -128 && s1->st_IntConst < 128) {
goto moveadd;
} else {
outop("add", 0, s1, d);
}
}
}
} else { /* not a constant */
Assert(s1->st_Size == d->st_Size);
if ((s1->st_Type == ST_Reg && s1->st_RegNo < RB_ADDR) || d->st_Type == ST_Reg) {
outop("add", 0, s1, d);
} else {
Stor stor;
moveadd:
LockStorage(d);
AllocDataRegister(&stor, d->st_Size);
asm_move(exp, s1, &stor);
UnlockStorage(d);
asm_add(exp, &stor, d, d);
FreeRegister(&stor);
}
}
}
void
asm_sub(exp, s1, s2, d)
Exp *exp;
Stor *s1;
Stor *s2;
Stor *d;
{
if (!SameStorage(s1,d) && !SameStorage(s2,d)) { /* 3 operand inst */
ThreeOp(exp, s1, s2, d, asm_sub);
return;
}
if (s1->st_Flags & SF_LEA) {
Stor t;
LockStorage(s2);
AllocAddrRegister(&t);
asm_move(exp, s1, &t);
UnlockStorage(s2);
asm_sub(exp, &t, s2, d);
FreeRegister(&t);
return;
}
if (s2->st_Flags & SF_LEA) {
Stor t;
LockStorage(s1);
AllocAddrRegister(&t);
asm_move(exp, s2, &t);
UnlockStorage(s1);
asm_sub(exp, s1, &t, d);
FreeRegister(&t);
return;
}
if (s1->st_Type == ST_IntConst) { /* #val - s2 */
Stor stor;
Assert(SameStorage(s2,d));
if (s2->st_Type == ST_Reg) {
asm_neg(exp, d, d);
asm_add(exp, s1, d, d);
return;
}
LockStorage(s2);
AllocDataRegister(&stor, d->st_Size);
asm_move(exp, s1, &stor);
UnlockStorage(s2);
asm_sub(exp, &stor, s2, &stor);
asm_move(exp, &stor, d);
FreeRegister(&stor);
return;
}
if (s2->st_Type == ST_IntConst) { /* s1 - #val */
Assert(SameStorage(s1,d));
if (s2->st_IntConst == 0)
return;
if (s2->st_IntConst >= -8 && s2->st_IntConst <= 8) {
if (s2->st_IntConst > 0) {
outop("subq", 0, s2, d);
} else {
s2->st_IntConst = -s2->st_IntConst;
outop("addq", 0, s2, d);
s2->st_IntConst = -s2->st_IntConst;
}
} else if (d->st_Type == ST_Reg && d->st_RegNo >= RB_ADDR) {
outop("suba", SizeFit(s2->st_IntConst), s2, d);
} else {
/*
* use moveq/sub if possible longword
*/
if (d->st_Size == 4 && s2->st_IntConst >= -128 && s2->st_IntConst < 128) {
goto movesub;
} else {
outop("sub", 0, s2, d);
}
}
return;
}
if (SameStorage(s1,d)) { /* d = d - s2 */
if (d->st_Type == ST_Reg || (s2->st_Type == ST_Reg && s2->st_RegNo < RB_ADDR)) {
outop("sub", 0, s2, d);
} else {
Stor stor;
movesub:
LockStorage(d);
AllocDataRegister(&stor, d->st_Size);
asm_move(exp, s2, &stor);
UnlockStorage(d);
asm_sub(exp, d, &stor, d);
FreeRegister(&stor);
}
} else { /* s2 == d: d = s1 - d */
Stor stor;
if (d->st_Type == ST_Reg) {
asm_neg(exp, d, d);
outop("add", 0, s1, d);
return;
}
LockStorage(d);
AllocDataRegister(&stor, d->st_Size);
asm_move(exp, s1, &stor);
UnlockStorage(d);
asm_sub(exp, &stor, d, &stor);
asm_move(exp, &stor, d);
FreeRegister(&stor);
}
return;
}
/*
* eadnok == 0 Dn,<ea>
* == 1 Dn,<ea> or <ea>,Dn
*/
Local void
asm_gen_logic_class(exp, str, bitstr, bitinverse, s1, s2, d, eadnok)
Exp *exp;
char *str;
char *bitstr;
Stor *s1;
Stor *s2;
Stor *d;
long bitinverse;
long eadnok;
{
if ((s1->st_Flags & SF_LEA) || (s1->st_Type == ST_Reg && s1->st_RegNo >= RB_ADDR)) {
Stor d1;
LockStorage(s2);
AllocDataRegister(&d1, 4);
UnlockStorage(s2);
asm_move(exp, s1, &d1);
asm_gen_logic_class(exp, str, bitstr, bitinverse, &d1, s2, d, eadnok);
FreeRegister(&d1);
return;
}
if ((s2->st_Flags & SF_LEA) || (s2->st_Type == ST_Reg && s2->st_RegNo >= RB_ADDR)) {
Stor d2;
LockStorage(s1);
AllocDataRegister(&d2, 4);
UnlockStorage(s1);
asm_move(exp, s2, &d2);
asm_gen_logic_class(exp, str, bitstr, bitinverse, s1, &d2, d, eadnok);
FreeRegister(&d2);
return;
}
if (d->st_Type == ST_Reg && d->st_RegNo >= RB_ADDR) { /* addr reg is dest */
Stor td;
AllocDataRegister(&td, d->st_Size);
asm_gen_logic_class(exp, str, bitstr, bitinverse, s1, s2, &td, eadnok);
asm_move(exp, &td, d);
FreeRegister(&td);
return;
}
if (SameStorage(s2, d)) {
SWAPS(s1,s2);
}
if (SameStorage(s1, d)) {
if (s2->st_Type == ST_IntConst) {
short n;
if ((n = PowerOfTwo(s2->st_IntConst ^ bitinverse)) >= 0) {
Stor con;
long size;
AllocConstStor(&con, n, &LongType);
if (d->st_Type == ST_Reg && d->st_RegNo < RB_ADDR) {
outop(bitstr, 4, &con, d);
} else {
long bits = d->st_Size * 8;
con.st_IntConst &= 7;
Assert((d->st_Flags & SF_LEA) == 0);
switch(d->st_Type) {
case ST_PtrConst:
case ST_RelArg:
case ST_RelReg:
case ST_RegIndex:
case ST_RelLabel:
case ST_RelName:
if (n < bits) {
size = d->st_Size;
d->st_Size = 1;
d->st_Offset += (bits-1-n) >> 3;
outop(bitstr, 1, &con, d);
d->st_Offset -= (bits-1-n) >> 3;
d->st_Size = size;
} else {
yerror(exp->ex_LexIdx, EWARN_CONSTANT_OUT_OF_RANGE, n);
}
return;
default:
dbprintf(("Illegal destination type %d\n", d->st_Type));
Assert(0);
}
}
return;
}
/*
* s2 is an integer constant. If the destination argument is a
* long and s2 is in the moveq range it is faster and smaller to
* move the constant into a data register first.
*/
if (d->st_Size == 4 && s2->st_IntConst >= -128 && s2->st_IntConst < 128) {
Stor tmp;
LockStorage(d);
AllocDataRegister(&tmp, d->st_Size);
UnlockStorage(d);
asm_move(exp, s2, &tmp);
outop(str, 0, &tmp, d);
FreeRegister(&tmp);
} else {
outop(str, 0, s2, d);
}
return;
} else if (s2->st_Type == ST_Reg && s2->st_RegNo < RB_ADDR) {
outop(str, 0, s2, d);
return;
} else if (d->st_Type == ST_Reg && d->st_RegNo < RB_ADDR) {
if (eadnok) {
outop(str, 0, s2, d);
return;
}
}
}
if (d->st_Type == ST_Reg) {
if (s1->st_Type == ST_Reg && s1->st_RegNo < RB_ADDR && !SameStorage(s1,d)) {
LockStorage(s1);
asm_move(exp, s2, d);
UnlockStorage(s1);
outop(str, 0, s1, d);
return;
} else if (s2->st_Type == ST_Reg && s2->st_RegNo < RB_ADDR && !SameStorage(s2,d)) {
LockStorage(s2);
asm_move(exp, s1, d);
UnlockStorage(s2);
outop(str, 0, s2, d);
return;
}
}
/*
* better to load the constant into a register, long operations will
* benefit when we can use moveq. We have to a move anyway so otherwise
* it does not matter whether the constant is moved or the other operand.
*/
if (s1->st_Type == ST_IntConst)
SWAPS(s1,s2);
{
Stor tmp;
short useD;
LockStorage(s1);
if (d->st_Type == ST_Reg && d->st_RegNo < RB_ADDR && !SameRegister(s1, d)) {
useD = 1;
asm_move(exp, s2, d);
} else {
useD = 0;
AllocDataRegister(&tmp, s2->st_Size);
asm_move(exp, s2, &tmp);
}
UnlockStorage(s1);
if ((s1->st_Type == ST_Reg && s1->st_RegNo >= RB_ADDR) || (eadnok == 0 && s1->st_Type != ST_Reg)) {
Stor tmp2;
AllocDataRegister(&tmp2, s1->st_Size);
asm_move(exp, s1, &tmp2);
if (useD)
outop(str, 0, &tmp2, d);
else
outop(str, 0, &tmp2, &tmp);
FreeRegister(&tmp2);
} else {
if (useD)
outop(str, 0, s1, d);
else
outop(str, 0, s1, &tmp);
}
if (useD == 0) {
asm_move(exp, &tmp, d);
FreeRegister(&tmp);
}
}
}
void
asm_xor(exp, s1, s2, d)
Exp *exp;
Stor *s1;
Stor *s2;
Stor *d;
{
if (s2->st_Type == ST_IntConst && s2->st_IntConst == -1) {
Stor *t = s1;
s1 = s2;
s2 = t;
}
if (s1->st_Type == ST_IntConst && s1->st_IntConst == -1) {
if (SameStorage(s2, d)) {
outop("not", d->st_Size, NULL, d);
} else {
Stor t;
AllocDataRegister(&t, s2->st_Size);
asm_move(exp, s2, &t);
outop("not", t.st_Size, NULL, &t);
asm_move(exp, &t, d);
FreeRegister(&t);
}
return;
}
asm_gen_logic_class(exp, "eor", "bchg", 0, s1, s2, d, 0);
}
void
asm_and(exp, s1, s2, d)
Exp *exp;
Stor *s1;
Stor *s2;
Stor *d;
{
asm_gen_logic_class(exp, "and", "bclr", -1, s1, s2, d, 1);
}
void
asm_or(exp, s1, s2, d)
Exp *exp;
Stor *s1;
Stor *s2;
Stor *d;
{
asm_gen_logic_class(exp, "or", "bset", 0, s1, s2, d, 1);
}
/*
* test / and optimization, note that btst doesn't work with address
* registers so can't optimize for that (asm_gen_logic.. will produce
* illegal output if we were to try)
*/
void
asm_test_and(exp, s1, s2)
Exp *exp;
Stor *s1;
Stor *s2;
{
Stor d;
if (s2->st_Type == ST_IntConst)
SWAPS(s1,s2);
if (s1->st_Type == ST_IntConst && PowerOfTwo(s1->st_IntConst) >= 0) {
if (s2->st_Type != ST_Reg || s2->st_RegNo < RB_ADDR) {
asm_gen_logic_class(exp, "tstand-bad-1", "btst", 0, s1, s2, s2, 1);
return;
}
}
if (s1->st_Size != s2->st_Size)
yerror(exp->ex_LexIdx, EERROR_SIZE_MISMATCH, s1->st_Size, s2->st_Size);
AllocDataRegister(&d, s1->st_Size);
asm_gen_logic_class(exp, "and", "tstand-bad-2", 0, s1, s2, &d, 1);
FreeRegister(&d);
}
/*
* Switch on storage s. Cases are sorted. Cases outside the
* storage range of the storage are
* deleted.
*
* (1) Table lookup
* (2) Switch List
* (3) Binary search with switch list sub segments
*/
void
asm_switch(exp, num, cases, labels, deflabel)
Exp *exp;
long num;
long *cases;
long *labels;
long deflabel;
{
long minBound, maxBound;
Stor con;
Stor dtmp;
Stor *s = &exp->ex_Stor;
if (num == 0)
return;
minBound = 0x80000000;
maxBound = 0x7FFFFFFF;
if (s->st_Flags & SF_UNSIGNED) {
if (s->st_Size == 1) {
minBound = 0;
maxBound = 255;
} else if (s->st_Size == 2) {
minBound = 0;
maxBound = 65535;
}
} else {
if (s->st_Size == 1) {
minBound = -128;
maxBound = 127;
} else if (s->st_Size == 2) {
minBound = -32768;
maxBound = 32767;
}
}
/*
* Sort the cases, delete out of bounds cases
*/
SortCases(exp, cases, labels, num);
while (num && cases[0] < minBound) {
yerror(exp->ex_LexIdx, EWARN_CONSTANT_OUT_OF_RANGE, cases[0]);
--num;
++cases;
++labels;
}
while (num && cases[num-1] > maxBound) {
yerror(exp->ex_LexIdx, EWARN_CONSTANT_OUT_OF_RANGE, cases[num-1]);
--num;
}
if (num == 0) {
asm_branch(deflabel);
return;
}
AllocConstStor(&con, 0, &LongType);
if (num == 1) {
short cond = COND_EQ;
con.st_IntConst = cases[0];
con.st_Size = s->st_Size;
asm_cmp(exp, s, &con, &cond);
asm_condbra(cond, labels[0]);
asm_branch(deflabel);
return;
}
/*
* If switching on an unsigned quantity convert the values to
* equivalent signed quantities because this is an assumption
* of the switch code.
*/
if ((s->st_Flags & SF_UNSIGNED) && s->st_Size != 4) {
long i;
for (i = 0; i < num; ++i) {
if (s->st_Size == 1)
cases[i] = (char)cases[i];
else if (s->st_Size == 2)
cases[i] = (short)cases[i];
}
}
SortCases(exp, cases, labels, num);
minBound = cases[0]; /* boundry conditions */
maxBound = cases[num-1];
if (num > 8 && (ulong)(maxBound - minBound) < num * 2) {
long label = AllocLabel();
long i;
long j;
short cond;
AllocDataRegister(&dtmp, s->st_Size);
asm_move(exp, s, &dtmp);
if (minBound) {
con.st_IntConst = minBound;
con.st_Size = s->st_Size;
asm_sub(exp, &dtmp, &con, &dtmp);
}
/*
* range test
*/
cond = COND_GT | CF_UNS;
con.st_IntConst = maxBound - minBound;
con.st_Size = s->st_Size;
asm_cmp(exp, &dtmp, &con, &cond);
asm_condbra(cond, deflabel);
/*
* size = 1 case, then force size to 2. (can handle 8192 cases) XXX?
*/
if (s->st_Size == 1) {
con.st_IntConst = 0x00FF;
outop("and", 2, &con, &dtmp);
}
dtmp.st_Size = 2;
printf("\tasl.w\t#2,D%c\n", dtmp.st_RegNo + '0');
printf("\tjmp\tl%ld(pc,d%d.w)\n", label, dtmp.st_RegNo);
asm_label(label);
j = 0;
for (i = minBound; i <= maxBound; ++i) {
while (cases[j] != i) {
printf("\tjmp\tl%ld(pc)\n", deflabel);
if (i == maxBound || j > num)
{
dbprintf(("in switch, case [%ld] %ld", j, cases[j]));
Assert(0);
}
++i;
}
printf("\tjmp\tl%ld(pc)\n", labels[j++]);
}
if (j != num)
{
dbprintf(("in switch, aryend %ld/%ld", j, num));
Assert(0);
}
FreeRegister(&dtmp);
} else {
/*
* subtract / branch
*/
AllocDataRegister(&dtmp, s->st_Size);
asm_move(exp, s, &dtmp);
con.st_Size = s->st_Size;
SubDivideSwitch(exp, &dtmp, &con, 0, 0, num, deflabel, cases, labels);
FreeRegister(&dtmp);
}
/*
* If switching on an unsigned quantity we converted the quantities
* to signed for the switch op, but due to the possibility of
* retries we have to convert the objects back before returning.
*/
if ((s->st_Flags & SF_UNSIGNED) && s->st_Size != 4) {
long i;
for (i = 0; i < num; ++i) {
if (s->st_Size == 1)
cases[i] = (ubyte)cases[i];
else if (s->st_Size == 2)
cases[i] = (uword)cases[i];
}
}
}
/*
* SubDivideSwitch()
*
* Values are signed quantities
*
* This is used to execute a binary subdivision on a switch. If the
* last case tested was j then our current zero is j.
*
* To test case n- next we must add 5, bcs for higher cases and
* bcc for lower cases
*
* To test case n+ next we must subtract 5, bcc for higher cases and
* bcs for lower cases
*
* S
* .
* n- 5 -5
* .
* N <---- j 10 0
* .
* n+ 15 +5
* .
* X
*/
void
SubDivideSwitch(exp, s, c, j, is, ie, deflabel, cases, labels)
Exp *exp;
Stor *s;
Stor *c;
long j;
long is, ie, deflabel;
long *cases;
long *labels;
{
long i;
long ic;
long v;
short high_cond;
if (ie - is <= 4) {
for (i = is; i < ie; ++i) {
c->st_IntConst = (cases[i] - j); /* amount to subtract */
if (c->st_IntConst)
asm_sub(exp, s, c, s);
else
asm_test(exp, s);
asm_condbra(COND_EQ, labels[i]);
j += c->st_IntConst;
}
asm_branch(deflabel);
return;
}
/*
* Current center point is j, find new center point. If the absolute
* range is greater then the maximum positive signed quantity the
* storage can hold we use a CMP, otherwise we ADD or SUB to the
* new center point.
*
* When we use compare we must use BGE style branches since we are
* doing a signed compare for signed ranging. When we use an
* add or subtract we use BPL to get to the greater half (ignoring
* overflow) and are guarenteed the range will, by that time,
* be less then 2^(N-1)
*/
ic = (is + ie) >> 1; /* center point (index) */
v = 1 << ((s->st_Size << 3) - 1); /* max pos integer + 1 */
if ((ulong)(cases[ic] - cases[is]) >= (ulong)v || (ulong)(cases[ie-1] - cases[ic]) >= (ulong)v) {
high_cond = COND_GTEQ;
c->st_IntConst = cases[ic];
asm_cmp(exp, s, c, &high_cond);
/*
* j doesn't change (remains 0)
*/
} else {
c->st_IntConst = j - cases[ic];
if (c->st_IntConst)
asm_add(exp, s, c, s);
else
asm_test(exp, s);
j = cases[ic];
high_cond = COND_BPL;
}
asm_condbra(COND_EQ, labels[ic]);
i = AllocLabel();
asm_condbra(high_cond, i);
#ifdef NOTDEF
if (high_cond == COND_GTEQ)
printf("\tbpl\tl%ld\n", i);
else
printf("\tbmi\tl%ld\n", i);
#endif
SubDivideSwitch(exp, s, c, j, is, ic, deflabel, cases, labels);
asm_label(i);
SubDivideSwitch(exp, s, c, j, ic+1, ie, deflabel, cases, labels);
}
#define SWAPL(l1,l2) { long lt = l1; l1 = l2; l2 = lt; }
Local void
SortCases(exp, cases, labels, num)
Exp *exp;
long *cases;
long *labels;
long num;
{
long sv;
long si;
long sj;
loop:
switch(num) {
case -1:
case 0:
case 1:
break;
case 2:
if (cases[0] > cases[1]) {
SWAPL(cases[0], cases[1]);
SWAPL(labels[0], labels[1]);
}
break;
default:
sv = cases[num>>1]; /* split value */
for (si = sj = 0; si < num; ++si) {
if (cases[si] < sv) {
SWAPL(cases[si], cases[sj]);
SWAPL(labels[si], labels[sj]);
++sj;
}
}
if (sj == 0) { /* sv smallest */
if ((si = num >> 1) != 0) {
SWAPL(cases[0], cases[si]);
SWAPL(labels[0], labels[si]);
}
++cases;
++labels;
--num;
goto loop;
}
if (sj == num) { /* sv largtest */
if ((si = num >> 1) != num - 1) {
SWAPL(cases[num-1], cases[si]);
SWAPL(labels[num-1], labels[si]);
}
--num;
goto loop;
}
SortCases(exp, cases, labels, sj);
SortCases(exp, cases + sj, labels + sj, num - sj);
}
{
long *cp = cases;
sv = *cp;
for (si = num - 2; si >= 0; --si, ++cp) {
sj = cp[1];
if (sv == sj)
break;
sv = sj;
}
}
if (si >= 0)
yerror(exp->ex_LexIdx, EERROR_CASE_REPEATED, sv, sv);
}
long
SizeFit(v)
long v;
{
if (v >= -32768 && v < 32768)
return(2);
return(4);
}
long
SizeFitSU(v, s)
long v;
Stor *s;
{
if (s->st_Flags & SF_UNSIGNED) {
if ((ulong)v < 65536)
return(2);
} else {
if (v >= -32768 && v < 32768)
return(2);
}
return(4);
}
void
asm_end()
{
long cp, ep;
printf("\tEND\n");
cp = ftell(stdout);
fseek(stdout, 0L, 2);
ep = ftell(stdout);
if (cp > 0 && ep > 0 && cp < ep) {
fseek(stdout, cp, 0);
while (cp < ep) {
fputc('\n', stdout);
++cp;
}
}
}